package pl.enigmatic.util;

import java.lang.reflect.Array;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

import pl.enigmatic.math.MathUtil;


public class Strings {

	private Strings() {}

	/**
	 * Sets the first letter in the word to upper case.
	 * @param field
	 * @return
	 */
	public final static String firstUpper(final String field) {
		if (field == null || field.equals("")) { return ""; }
		return Character.toUpperCase(field.charAt(0)) + field.substring(1);
	}

	/**
	 * Sets the first letter in the word to lower case
	 */
	public static final String firstLower(final String s) {
		if (s == null || s.equals("")) { return ""; }
		return Character.toLowerCase(s.charAt(0)) + s.substring(1);
	}

	public static boolean empty(final String str) {
		return str == null || "".equals(str);
	}

	public static boolean nonEmpty(final String str) {
		return !empty(str);
	}

	public static String trim(final String str) {
		return str != null ? str.trim() : "";
	}

	public static char[] getChars(final String string) {
		final char[] res = new char[string.length()];
		for (int i = 0; i < string.length(); i++) {
			res[i] = string.charAt(i);
		}
		return res;
	}

	public static String toString(final Object obj, final String comma, final String left, final String right) {
		if (obj != null && obj.getClass().isArray()) {
			String res = left;
			if (Array.getLength(obj) > 0) {
				for (int i = 0; i < Array.getLength(obj); i++) {
					res += toString(Array.get(obj, i)) + comma;
				}
				res = res.substring(0, res.length() - comma.length());
			}
			res += right;
			return res;
		} else {
			return String.valueOf(obj);
		}
	}

	public static String toString(final Object obj, final Object... properties) {
		final StringBuilder builder = new StringBuilder();
		builder.append(obj.getClass().getName());
		builder.append(" [");
		for (int i = 0; i < properties.length;) {
			builder.append(properties[i++]);
			builder.append("=");
			builder.append(properties[i++]);
			if (i < properties.length) {
				builder.append(", ");
			}
		}
		builder.append("]");
		return builder.toString();
	}

	public static String toString(final Object obj, final String comma) {
		return toString(obj, comma, "[", "]");
	}

	public static String toString(final Object obj) {
		return toString(obj, ", ");
	}

	public static Set<Character> getCharsSet(final String text) {
		final Set<Character> set = new HashSet<Character>();
		for (final char c : getChars(text)) {
			set.add(c);
		}
		return set;
	}

	public static int clamp(final String string, final int index) {
		return MathUtil.clamp(0, string.length() - 1, index);
	}

	public static int wrap(final String string, final int index) {
		return MathUtil.wrap(0, string.length() - 1, index);
	}

	public static boolean upperStarted(final String s) {
		return nonEmpty(s) && Character.isUpperCase(s.charAt(0));
	}

	public static boolean lowerStarted(final String s) {
		return nonEmpty(s) && Character.isLowerCase(s.charAt(0));
	}

	public static String ending(final String string, final int count) {
		return string.substring(clamp(string, string.length() - count), string.length());
	}

	public static String beginning(final String string, final int cut) {
		return string.substring(0, string.length() - cut);
	}

	public static String repeat(final String string, final int count) {
		final StringBuilder builder = new StringBuilder();
		for (int i = 0; i < count; i++) {
			builder.append(string);
		}
		return builder.toString();
	}

	public static String removeTabs(final String string) {
		return string.replaceAll("[\\t]+", "");
	}

	public static String removeNewLines(final String string) {
		return string.replaceAll("[\\n]+", "");
	}

	public static String trimAllWhitespaces(final String string) {
		return string.replaceAll("[ \\t\\n]+", " ").trim();
	}

	public static String encode(String string) {
		string = string.replaceAll("\\\\", "\\\\\\\\");
		string = string.replaceAll("\\n", "\\\\n");
		string = string.replaceAll("\\t", "\\\\t");
		string = string.replaceAll(" ", "\\\\ ");
		return string;
	}

	public static String decode(String string) {
		string = string.replaceAll("\\\\n", "\n");
		string = string.replaceAll("\\\\t", "\t");
		string = string.replaceAll("\\\\ ", " ");
		string = string.replaceAll("\\\\\\\\", "\\\\");
		return string;
	}

	public static String concatenate(String separator, final String... strings) {
		final StringBuilder builder = new StringBuilder();
		separator = denullify(separator);
		for (final String string : strings) {
			if (nonEmpty(string)) {
				builder.append(string);
				builder.append(separator);
			}
		}
		final String string = builder.toString();
		return empty(string) ? string : beginning(string, separator.length());
	}

	public static String[] toUpperCase(final String[] strings) {
		for (int i = 0; i < strings.length; i++) {
			if (strings[i] != null) {
				strings[i] = strings[i].toUpperCase();
			}
		}
		return strings;
	}

	public static String[] toLowerCase(final String[] strings) {
		for (int i = 0; i < strings.length; i++) {
			if (strings[i] != null) {
				strings[i] = strings[i].toLowerCase();
			}
		}
		return strings;
	}

	public static String denullify(final String string) {
		return empty(string) ? "" : string;
	}

	public static String prefix(final String base, final String separator) {
		if (base.contains(separator)) {
			return base.substring(0, base.indexOf(separator));
		} else {
			return base;
		}
	}

	public static String prefixed(final String base, final String separator) {
		if (base.contains(separator)) {
			return base.substring(base.indexOf(separator) + 1);
		} else {
			return base;
		}
	}

	public static String suffix(final String base, final String separator) {
		if (base.contains(separator)) {
			return base.substring(base.lastIndexOf(separator) + 1);
		} else {
			return base;
		}
	}

	public static String suffixed(final String base, final String separator) {
		if (base.contains(separator)) {
			return base.substring(0, base.lastIndexOf(separator));
		} else {
			return base;
		}
	}

	public static boolean equals(final String x, final String y) {
		return x == null && y == null || x != null && x.equals(y);
	}

	public static String table(final int dist, final String[]... cols) {
		final StringBuilder builder = new StringBuilder();
		final int start[] = new int[cols.length - 1];
		for (int i = 0; i < start.length; i++) {
			start[i] = 0;
			for (int j = 0; j < cols[i].length; j++) {
				start[i] = Math.max(start[i], cols[i][j].length());
			}
			start[i] += dist;
		}
		for (int i = 0; i < cols[0].length; i++) {
			for (int j = 0; j < cols.length - 1; j++) {
				builder.append(cols[j][i]);
				builder.append(repeat(" ", start[j] - cols[j][i].length()));
			}
			builder.append(cols[cols.length - 1][i]);
			builder.append("\n");
		}
		return beginning(builder.toString(), 1);
	}

	public static String shift(final String string, final String tabs) {
		final StringBuilder builder = new StringBuilder();
		for (final String line : string.split("\n")) {
			builder.append(tabs);
			builder.append(line);
			builder.append("\n");
		}
		return beginning(builder.toString(), 1);
	}

	public static boolean isNumeric(final String string) {
		return Pattern.matches("[0123456789]*", string);
	}

	public static String reverse(final String string) {
		if (string == null) { return string; }
		final StringBuilder s = new StringBuilder();
		for (int i = 0; i < string.length(); i++) {
			s.insert(0, string.charAt(i));
		}
		return s.toString();
	}

}
